<?php
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>. 
//
// This file was modified by contributors of "BOINC Web Tweak" project.

// actions:
// (none)
//	  if have a snapshot, show start/end
//	  show form to get new snapshot
// snap_action
//	  make new snapshot and go to...
// map
//	  show a map;
//	  show form to set or change filter or breakdown
//
// columns:
// name
// type
// breakdown category (if using breakdown)
// nviews (as number)
// outcome (as color-coded bar graph: green=next, yellow=back, red=none)
// time (bar graph of log(t))
// score (bar graph)
//
// what's shown:
// lessons: nviews, outcome, time
// exercise: nviews, outcome, time, score
// exercise answer: nviews, outcome, time
// exercise set: score
//
// When breakdown is used, each of above has N lines
// Total, followed by each breakdown category

require_once("../inc/bolt_util_ops.inc");
require_once("../inc/bolt_db.inc");
require_once("../inc/bolt_cat.inc");
require_once("../inc/bolt.inc");
require_once("../inc/bolt_snap.inc");

// the following are to minimize argument passing

$snap = null;
$course_id = 0;
$top_unit = null;
$filter = null;
$filter_cat = null;
$breakdown = null;
$breakdown_cat = null;

function show_snap_form() {
	global $course_id;

	admin_page_head("Data snapshot");
	$s = read_map_snapshot($course_id);

	if ($s) {
		$end = date_str($s->time);
		echo "
			A data snapshot exists for the $s->dur days prior to $end.
		";
		show_button(
			"bolt_map.php?action=map&course_id=$course_id",
			"Use this snapshot",
			"Use this snapshot"
		);
	} else {
		echo "There is currently no snapshot.";
	}
	echo "
		<form action=bolt_map.php>
		<input type=hidden name=action value=snap_action>
		<input type=hidden name=course_id value=$course_id>
		Create a new snapshot using data from the last
		<input name=dur value=7> days.
		<input type=submit value=OK>
		</form>
	";
	admin_page_tail();
}

function snap_action() {
	global $course_id;
	global $top_unit;

	$dur = get_int('dur');
	$s = write_map_snapshot($course_id, $dur);
	show_map();
}

function spaces($level) {
	$x = "";
	for ($i=0; $i<$level; $i++) {
		$x .= "&nbsp;&nbsp;&nbsp;&nbsp;";
	}
	return $x;
}

// filter arrays of anything that has a user_id field
// (view, xset_result, question)
//
function filter_array($array) {
	global $snap, $filter, $filter_cat, $breakdown, $breakdown_cat;

	if (!$filter && !$breakdown) return $array;
	$x = array();
	foreach ($array as $y) {
		if (!array_key_exists($y->user_id, $snap->users)) continue;
		$u = $snap->users[$y->user_id];
		if ($filter && $filter->categorize($u) != $filter_cat) {
			continue;
		}
		if ($breakdown && $breakdown_cat) {
			if ($breakdown->categorize($u) != $breakdown_cat) {
				continue;
			}
		}
		$x[] = $y;
	}
	return $x;
}

function avg_score($array) {
	$sum = 0;
	$n = count($array);
	if ($n ==0) return 0;
	foreach ($array as $a) {
		$sum += $a->score;
	}
	return $sum/count($array);
}

function avg_time($views) {
	$sum = 0;
	$n = 0;
	foreach ($views as $v) {
		if ($v->start_time && $v->end_time) {
			$sum += $v->end_time - $v->start_time;
			$n++;
		}
	}
	if ($n ==0) return 0;
	return $sum/$n;
}

function outcomes($views) {
	$x = array();
	$x[0] = 0;
	$x[1] = 0;
	$x[2] = 0;

	foreach ($views as $v) {
		switch ($v->action) {
		case BOLT_ACTION_NONE: $x[0]++; break;
		case BOLT_ACTION_NEXT: $x[1]++; break;
		case BOLT_ACTION_SUBMIT: $x[1]++; break;
		default: $x[2]++; break;
		}
	}
	return $x;
}

function get_nquestions($unit, $mode) {
	global $snap;

	if (array_key_exists($unit->name, $snap->questions)) {
		$a = filter_array($snap->questions[$unit->name]);
		$n = 0;
		foreach ($a as $q) {
			if ($q->mode == $mode) $n++;
		}
		return $n;
	}
	return 0;
}

function get_views($unit, $mode) {
	global $snap;

	$y = array();
	if (array_key_exists($unit->name, $snap->views)) {
		$a = filter_array($snap->views[$unit->name]);
		foreach ($a as $x) {
			if ($x->mode == $mode) $y[] = $x;
		}
	}
	return $y;
}

function get_results($unit) {
	global $snap;

	if (array_key_exists($unit->name, $snap->results)) {
		return filter_array($snap->results[$unit->name]);
	}
	return array();
}

function get_xset_results($unit) {
	global $snap;

	if (array_key_exists($unit->name, $snap->xset_results)) {
		return filter_array($snap->xset_results[$unit->name]);
	}
	return array();
}

function class_name($class) {
	switch ($class) {
	case "BoltSequence": return "sequence";
	case "BoltSelect": return "select";
	case "BoltLesson": return "lesson";
	case "BoltExercise": return "exercise";
	case "BoltExerciseSet": return "exercise set";
	case "BoltRandom": return "random";
	}
}

$rownum = 0;

function show_unit_row($unit, $class, $level, $is_answer) {
	global $breakdown, $breakdown_cat;
	global $rownum, $course_id;

	$a = $is_answer?" (answer)":"";
	$j = ($rownum++)%2;
	echo "<tr class=row$j>";
	if ($breakdown && $breakdown_cat) {
		echo "
			<td><br></td>
			<td><br></td>
		";
	} else  {
		$c = class_name($class);
		echo "
			<td><b>".spaces($level)."$unit->name</b></td>
			<td>$c $a</td>
		";
	}
	if ($breakdown) {
		if ($breakdown_cat) {
			echo "<td>$breakdown_cat</td>\n";
		} else {
			echo "<td>Total</td>\n";
		}
	}
	switch ($class) {
	case "BoltLesson":
		$mode = BOLT_MODE_LESSON;
		$views = get_views($unit, $mode);
		$n = count($views);
		$out = outcomes($views);
		$t = avg_time($views);
		echo "<td>$n</td>";
		$n = get_nquestions($unit, $mode);
		if ($n) {
			echo "<td><a href=bolt_map.php?action=questions&course_id=$course_id&name=$unit->name&mode=$mode".filter_url().">$n</a></td>\n";
		} else {
			echo "<td>0</td>\n";
		}

		echo outcome_graph($out, 200);
		echo empty_cell();
		echo time_graph($t, 200);
		break;
	case "BoltExercise":
		$mode = $is_answer?BOLT_MODE_ANSWER:BOLT_MODE_SHOW;
		$views = get_views($unit, $mode);
		$n = count($views);
		$out = outcomes($views);
		$t = avg_time($views);
		echo "<td>$n</td>";
		$n = get_nquestions($unit, $mode);
		if ($n) {
			echo "<td><a href=bolt_map.php?action=questions&course_id=$course_id&name=$unit->name&mode=$mode>$n</a></td>\n";
		} else {
			echo "<td>0</td>\n";
		}
		echo outcome_graph($out, 200);
		if ($is_answer) {
			echo empty_cell();
		} else {
			$results = get_results($unit);
			$score = avg_score($results);
			echo score_graph($score, 200);
		}
		echo time_graph($t, 200);
		break;
	case "BoltExerciseSet":
		$xr = get_xset_results($unit);
		$n = count($xr);
		echo "<td>$n</td>";
		echo empty_cell();
		echo empty_cell();
		$score = avg_score($xr);
		echo score_graph($score, 200);
		echo empty_cell();
		break;
	default:
		echo empty_cell();
		echo empty_cell();
		echo empty_cell();
		echo empty_cell();
		echo empty_cell();
	}
	echo "</tr>\n";
}

function breakdown_class($class) {
	switch ($class) {
	case "BoltLesson":
	case "BoltExercise":
	case "BoltExerciseSet":
		return true;
	}
	return false;
}

function show_unit($unit, $level) {
	global $snap, $filter, $filter_cat, $breakdown, $breakdown_cat;

	$class = get_class($unit);
	$breakdown_cat = null;
	show_unit_row($unit, $class, $level, false);
	if ($breakdown && breakdown_class($class)) {
		foreach ($breakdown->categories() as $c) {
			$breakdown_cat = $c;
			show_unit_row($unit, $class, $level, false);
		}
	}

	// if exercise, show answer page views
	//
	if ($class == "BoltExercise") {
		$breakdown_cat = null;
		show_unit_row($unit, $class, $level, true);
		if ($breakdown) {
			foreach ($breakdown->categories() as $c) {
				$breakdown_cat = $c;
				show_unit_row($unit, $class, $level, true);
			}
		}
	}
}

function show_unit_recurse($unit, $level) {
	show_unit($unit, $level);
	if ($unit->is_item) return;
	foreach ($unit->units as $u) {
		show_unit_recurse($u, $level+1);
	}
}

function show_map() {
	global $snap, $course_id, $top_unit, $filter, $filter_cat, $breakdown;
	global $course;

	get_filters_from_form();

	admin_page_head("Course map for '$course->name'");
	bolt_style();
	$snap = read_map_snapshot($course_id);
	start_table();
	echo "
		<tr>
			<th>Name</th>
			<th>Type</th>
	";
	if ($breakdown) {
		echo "<th>Group</th>";
	}
	echo "
			<th>Views</th>
			<th>Questions</th>
			<th>Outcome<br>
				<span class=green>Next</span>
				<span class=yellow>Back</span>
				<span class=red>None</span>
			</th>
			<th>Score</th>
			<th>Time</th>
		</tr>
	";
	show_unit_recurse($top_unit, 0);
	echo "
		</table>
		<form action=bolt_map.php>
		<input type=hidden name=action value=map>
		<input type=hidden name=course_id value=$course_id>
		<table width=600><tr><td valign=top>
	";
	filter_form($filter?$filter->name():"", $filter_cat);
	echo "</td><td valign=top>";
	breakdown_form($breakdown?$breakdown->name():"");
	echo "
		</td></tr></table>
		<p>
		<input type=submit value=OK>
		</form>
	";
	admin_page_tail();
}

function show_questions() {
	global $course_id;

	$name = get_str('name');
	$mode = get_int('mode');
	get_filters_from_form();
	$snap = read_map_snapshot($course_id);
	$qs = $snap->questions[$name];
	admin_page_head("Questions about $name");
	start_table();
	echo "<tr>
		<th>When</th>
		<th>Who</th>
		<th>Question</th>
		</tr>
	";
	foreach ($qs as $q) {
		if ($q->mode != $mode) continue;
		$user = $snap->users[$q->user_id];
		echo "<tr>
			<td>".time_str($q->create_time)."</td>
			<td><a href=student>$user->name</td>
			<td>$q->question</td>
			</tr>
		";
	}
	end_table();
	admin_page_tail();
}

$course_id = get_int('course_id');
$course = BoltCourse::lookup_id($course_id);
if (!$course) error_page("no course");
$top_unit = require_once($course->doc_file());

$action = get_str('action', true);
switch ($action) {
case "":
	show_snap_form();
	break;
case "snap_action":
	snap_action();
	break;
case "map":
	show_map();
	break;
case "questions":
	show_questions();
	break;
default:
	error_page("Unknown action $action");
}

?>
